﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Diagnostics;
using jaysJourney_v01.Utils;
using jaysJourney_v01.States;
using Microsoft.Xna.Framework.Audio;

namespace jaysJourney_v01.Common
{
    class Jay : Collidable
    {
        /**
         * MEMBER VARIABLES
         * */

        // From Parent

        private JaysJourneyMain game;
        private SpriteBatch parentSpriteBatch;
        private ContentManager contentManager;
        private PlayState playState;

        // Common
        public int JAY_WIDTH = 30;
        public int JAY_HEIGHT = 63;

        private Collider collider;
        private List<Point> colPoints;

        private SpriteAnimator idleAnimation;
        private SpriteAnimator deathAnimation;
        private SpriteAnimator startAnimation;
        private SpriteAnimator winAnimation;

        private float delayCounter = 0;
        private bool gameIsLocked = false;

        private bool jayIsDead = false;
        private bool jayHasWon = false;

        // Movement

        public Vector2 newPosition;
        public float curRunningSpeed = 0.0f;

        private bool lookRight = true;

        private SpriteAnimator walkingAnimation;
        private Vector2 oldPosition;

        // Jumping

        public Boolean isJumping = false;
        public Boolean isKeyUp = true;
        public float curFallSpeed = 0.0f;

        private SpriteAnimator jumpAnimation;
        private SpriteAnimator fallAnimation;

        // Audio

        private SoundEffect landSoundEffect;        

        //rumbleTimer
        //private static float rumbleTimer = 0.0f;

        /**
         * METHODS
         * */

        // Constructor

        public Jay(Vector2 position, PlayState pl)
        {
            // From Parent

            this.game = pl.getGame();
            this.parentSpriteBatch = pl.getMainSpriteBatch();
            this.contentManager = game.Content;
            this.collider = pl.getCollider();
            this.playState = pl;

            // Start Position

            this.newPosition = position;

            // Load Sprites

            this.walkingAnimation = new SpriteAnimator(parentSpriteBatch, this.contentManager.Load<Texture2D>(@"Textures\Jay\jay_run"), 16, 8, 64, 64, 512, 64, true, true);       //fps from 8 to 16
            this.walkingAnimation.play();

            this.jumpAnimation = new SpriteAnimator(parentSpriteBatch, this.contentManager.Load<Texture2D>(@"Textures\Jay\jay_jump"), 16, 5, 64, 64, 320, 64, false, true);        //from 8 to 16
            this.jumpAnimation.play();

            this.fallAnimation = new SpriteAnimator(parentSpriteBatch, this.contentManager.Load<Texture2D>(@"Textures\Jay\jay_fly"), 16, 3, 64, 64, 192, 64, true, true);           //from 8 to 16
            this.fallAnimation.play();

            this.idleAnimation = new SpriteAnimator(parentSpriteBatch, this.contentManager.Load<Texture2D>(@"Textures\Jay\jay_idle"), 8, 6, 64, 64, 384, 64, true, true);
            this.idleAnimation.play();

            this.deathAnimation = new SpriteAnimator(parentSpriteBatch, this.contentManager.Load<Texture2D>(@"Textures\Jay\jay_death"), 12, 8, 64, 64, 512, 64, false, true);
            this.deathAnimation.play();

            this.startAnimation = new SpriteAnimator(parentSpriteBatch, this.contentManager.Load<Texture2D>(@"Textures\Jay\jay_start"), 8, 8, 64, 64, 512, 64, false, true);
            this.startAnimation.play();

            this.winAnimation = new SpriteAnimator(parentSpriteBatch, this.contentManager.Load<Texture2D>(@"Textures\Jay\jay_win"), 16, 8, 64, 64, 512, 64, false, true);
            this.winAnimation.play();

            // Load Sounds

            this.landSoundEffect = contentManager.Load<SoundEffect>(@"Audio\Sounds\land");
        }

        // Main Draw Method

        public void Draw(GameTime gameTime, Vector2 curZero)
        {
            Vector2 newPos = this.newPosition - curZero;
            newPos.X -= this.JAY_WIDTH / 2; // remove transparent area

            if (this.jayIsDead && this.gameIsLocked) // Jay dies
            {
                this.delayCounter += gameTime.ElapsedGameTime.Milliseconds;
                this.deathAnimation.animate(gameTime, newPos, this.lookRight);
            }
            else if (this.jayHasWon && this.gameIsLocked) // Jay wins
            {
                this.winAnimation.animate(gameTime, newPos, this.lookRight);
            }
            else if (this.gameIsLocked) // Jay start
            {
                if(!this.startAnimation.hasFinished()) this.startAnimation.animate(gameTime, newPos, this.lookRight);
                else this.idleAnimation.animate(gameTime, newPos, this.lookRight);
            }
            else
            {
                if (this.curRunningSpeed == 0.0 && this.curFallSpeed == 0.0) // Jay is Standing
                {
                    this.idleAnimation.animate(gameTime, newPos, this.lookRight);
                }
                else if (this.curRunningSpeed != 0.0 && this.curFallSpeed == 0.0) // Jay is Running
                {
                    this.walkingAnimation.animate(gameTime, newPos, this.lookRight);
                }
                else if (this.curFallSpeed >= 0) // Jay is Jumping
                {
                    this.jumpAnimation.animate(gameTime, newPos, this.lookRight);
                }
                else if (this.curFallSpeed < 0) // Jay is Falling
                {
                    this.fallAnimation.animate(gameTime, newPos, this.lookRight);
                }
            }
        }

        // Main update Loop

        public void update(GameTime gameTime)
        {
            if (!playState.getStartDelay() && !this.jayHasWon)
            {
                KeyboardState keyboard = Keyboard.GetState();
                oldPosition = newPosition;
                GamePadState gamepad = GamePad.GetState(PlayerIndex.One);
                updateMovement(keyboard, gamepad);
                updateJumping(keyboard, gamepad);

                // calc collision
                Vector2 oldPos = this.newPosition;
                this.colPoints = collider.applyCollision(this, this.JAY_WIDTH, this.JAY_HEIGHT);

                // trigger collided blocks
                for (int i = 0; i < this.colPoints.Count; i++)
                {
                    playState.interactiveBlocks.triggerBlock(this.colPoints[i]);
                }

                // check borders
                if (this.newPosition.X < 0)
                {
                    this.newPosition.X = 0;
                    this.curRunningSpeed = 0;
                }
                else if (this.newPosition.X > ((this.playState.getLevel().XCOUNT - 1) * CONST.BLOCKSIZE))
                {
                    this.newPosition.X = ((this.playState.getLevel().XCOUNT - 1) * CONST.BLOCKSIZE);
                    this.curRunningSpeed = 0;
                }

                if (this.newPosition.Y < 0)
                {
                    this.newPosition.Y = 0;
                    this.curFallSpeed = 0;
                }

                // unten death triggern
                else if (this.newPosition.Y + 64 > ((this.playState.getLevel().YCOUNT) * CONST.BLOCKSIZE))
                {
                    this.killJay();
                }

                // reset jumping
                if (oldPos.Y < this.newPosition.Y) // if it would be 0, idleAnimation would be triggered for 1 frame
                {
                    this.curFallSpeed = -0.000000000000000001f;
                }
                if (oldPos.Y > this.newPosition.Y)
                {
                    if (this.curFallSpeed < -0.24f)
                    {
                        SoundEffectInstance sei = this.landSoundEffect.CreateInstance();
                        sei.Volume = this.playState.getStateManager().getGame().volumeLevel * 1.0f; //+specific volume parameter(float)
                        sei.Play();
                    }

                    this.curFallSpeed = 0;
                    if (this.isKeyUp)
                    {
                        this.isJumping = false;
                    }
                    this.jumpAnimation.reset();
                }

                // on death
                if (this.jayIsDead) this.deathLoop();
            }
        }

        // Move Jay

        private void updateMovement(KeyboardState keyboard, GamePadState gamepad)
        {
            if (!this.gameIsLocked && keyboard.IsKeyDown(Keys.Right) || gamepad.IsButtonDown(Buttons.DPadRight)) // Move Right
            {
                if (curRunningSpeed < 0) curRunningSpeed += CONST.GRAVITY; // double the speed for faster stopping
                if (curRunningSpeed <= CONST.MAX_MOVESPEED) curRunningSpeed += CONST.GRAVITY;
                this.lookRight = true; // lookDir
            }
            else if (!this.gameIsLocked && keyboard.IsKeyDown(Keys.Left) || gamepad.IsButtonDown(Buttons.DPadLeft)) // Move Left
            {
                if (curRunningSpeed > 0) curRunningSpeed -= CONST.GRAVITY; // double the speed for faster stopping
                if (curRunningSpeed >= -CONST.MAX_MOVESPEED) curRunningSpeed -= CONST.GRAVITY;
                this.lookRight = false; // lookDir
            }
            else if (this.gameIsLocked || !keyboard.IsKeyDown(Keys.Right) && !keyboard.IsKeyDown(Keys.Left) || (!gamepad.IsButtonDown(Buttons.DPadRight) && gamepad.IsButtonDown(Buttons.DPadLeft))) // slow down
            {
                if (curRunningSpeed <= -CONST.GRAVITY)
                {
                    if (this.gameIsLocked) curRunningSpeed += CONST.GRAVITY / 4; // less gravity on death for longer bone flights :P
                    else curRunningSpeed += CONST.GRAVITY;
                }
                else if (curRunningSpeed >= CONST.GRAVITY)
                {
                    if (this.gameIsLocked) curRunningSpeed -= CONST.GRAVITY / 4;
                    else curRunningSpeed -= CONST.GRAVITY;
                }
                else
                {
                    curRunningSpeed = 0;
                }
            }
            this.newPosition.X += curRunningSpeed; // calculate new position
        }

        // Jump Jay

        private void updateJumping(KeyboardState keyboard, GamePadState gamepad)
        {
            // Check Input
            if (!this.gameIsLocked && keyboard.IsKeyDown(Keys.Up) || gamepad.IsButtonDown(Buttons.DPadUp) || gamepad.IsButtonDown(Buttons.A))
            {
                if (this.isJumping == false && this.curFallSpeed == 0) // cant jump during fall
                {
                    this.isJumping = true;
                    this.isKeyUp = false;
                    this.curFallSpeed = CONST.MAX_JUMPSPEED;
                }
            }

            if (keyboard.IsKeyUp(Keys.Up) && gamepad.IsButtonUp(Buttons.DPadUp) && gamepad.IsButtonUp(Buttons.A))
            {
                this.isKeyUp = true;
            }

            if (this.curFallSpeed > CONST.MAX_FALLSPEED) this.curFallSpeed -= CONST.GRAVITY; // Let Gravity influence Jay
            this.newPosition.Y -= this.curFallSpeed; // calculate new position
        }

        public float getRunspeed()
        {
            return this.curRunningSpeed;
        }

        public float getFallspeed()
        {
            return this.curFallSpeed;
        }
        public Vector2 getPosition()
        {
            return this.newPosition;
            
        }
        public void setPosition(Vector2 Position)
        {
            this.newPosition = Position;
        }
        public void resetFallspeed()
        {
            this.curFallSpeed = 0;
        }
        public void resetRunspeed()
        {
            this.curRunningSpeed = 0;
        }
        public Vector2 getOldPosition()
        {
            return this.oldPosition;
        }

        public void killJay()
        {
            this.gameIsLocked = true;
            this.jayIsDead = true;
            this.playState.musicOff();
        }

        private void deathLoop()
        {
            if (this.delayCounter > CONST.DELAY_TIME && this.gameIsLocked && this.jayIsDead)
            {
                this.delayCounter = 0;
                this.gameIsLocked = false;

                if (this.game.getStateManager().getLastActiveState() == "editorState")
                {
                    this.game.getStateManager().setActiveState("editorState");
                }
                else
                {
                    playState.getLvlData().setNumStarts(playState.getLvlData().getNumStarts() + 1);
                    if (!WebSaver.worldUpdate(playState.getLvlData().getLevel(), playState.getLvlData().getNumStarts(), playState.getLvlData().getNumRates(), playState.getLvlData().getCurRating(), playState.getLvlData().getBestTime(), playState.getLvlData().getRecordHolder(), playState.getLvlData().getAvgBestTime()))
                    {
                        // Error database connection
                        this.game.pushMessage("Could not connect to the Database, you got disconnected!", true);
                        this.game.getStateManager().setActiveState("loginState");
                    }
                    else
                    {
                        this.game.getStateManager().setActiveState("deathState");
                    }
                }
            }
        }

        public void winJay()
        {
            this.jayHasWon = true;
            this.gameIsLocked = true;
            this.playState.musicOff();
        }

        public void setGameLock(bool isLocked)
        {
            this.gameIsLocked = isLocked;
        }

        public bool isJayDead()
        {
            return this.jayIsDead;
        }
    }
}